#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <directfb_util.h>
#include "dtk.h"
#include "dtk_debug.h"
#include "dtk_actor.h"
#include "dtk_event.h"
#include "dtk_normal_layout.h"

DString dtk_actor_get_type (DTKActor* self)
{
    if (self == NULL)
    {
        return NULL;
    }

    return self->type;
}

void dtk_actor_set_position (DTKActor* self, DInt x, DInt y)
{
    if (self == NULL || (self->rect.x == x && self->rect.y == y))
    {
        return;
    }

    self->rect.x = x;
    self->rect.y = y;    

    if (self->parent)
    {
        self->abs_point.x = x + self->parent->abs_point.x;
        self->abs_point.y = x + self->parent->abs_point.y;
    }
    else
    {
        self->abs_point.x = x;
        self->abs_point.y = y;
    }

    dtk_layout_set_position (self->layout, x, y);

    dtk_actor_update(self, NULL, DTK_FALSE);
    return;
}

void dtk_actor_get_position (DTKActor* self, DInt* x, DInt* y)
{
    if (self == NULL)
    {
        return;
    }

    if (x)
        *x = self->rect.x;

    if (y)
        *y = self->rect.y;
    
    return;
}

void dtk_actor_set_size (DTKActor* self, DInt width, DInt height)
{
    if (self == NULL)
    {
        return;
    }

    dtk_layout_set_fix_size(self->layout, width, height);

    dtk_actor_update(self, NULL, DTK_FALSE);
    return;
}

void dtk_actor_set_default_size (DTKActor* self, DInt width, DInt height)
{
    if (self == NULL)
    {
        return;
    }

    dtk_layout_set_default_size(self->layout, width, height);

    dtk_actor_update(self, NULL, DTK_FALSE);
    return;
}

DBoolean dtk_actor_get_size (DTKActor* self, DInt* width, DInt* height)
{
    if (self == NULL)
    {
        return;
    }

    return dtk_layout_get_fix_size (self->layout, width, height);
}

void dtk_actor_set_opacity(DTKActor* self, DInt opacity)
{
    if (NULL == self || opacity < 0 || opacity > 0xff)
    {
        return;
    }

    self->opacity = opacity;

    return;
}

DInt dtk_actor_get_opacity(DTKActor* self)
{
    if (NULL == self)
    {
        return 0;
    }

    return self->opacity;
}

void dtk_actor_set_parent (DTKActor* self, DTKActor* parent)
{
    if (NULL == self || NULL == parent || self == parent)
    {
        return;
    }

    if (self->parent != NULL)
    {
        DirectFBError("Cannot set a parent on an actor which has a parent.\n"
		 "You must use dtk_actor_unparent() first.\n", DFB_OK);
    }

    if (self->flags & DTK_ACTOR_IS_TOPLEVEL)
    {
        DirectFBError ("Cannot set a parent on a toplevel actor\n", DFB_OK);
        return;
    }

    if (parent->flags & DTK_ACTOR_IN_DESTRUCTION)
    {
        DirectFBError ("Cannot set a parent currently being destroyed", DFB_OK);
        return;
    }

    self->parent = parent;
    
    return;
}


DTKActor* dtk_actor_get_parent (DTKActor* self)
{
    if (NULL == self)
    {
        return NULL;
    }
    
    return self->parent;
}

void dtk_actor_unparent (DTKActor* self)
{
    if (self == NULL || self->parent == NULL)
    {
        return;
    }

    self->parent = NULL;

    return;
}

void dtk_actor_show (DTKActor* self)
{
    if (self == NULL)
    {
        return;
    }

    self->visible = DTK_TRUE;

    if (self->funcs.show)
        self->funcs.show (self);

    dtk_actor_update(self, NULL, DTK_FALSE);
    return;
}

void dtk_actor_show_all (DTKActor* self)
{
    DTKActorCallback* funcs;
    
    if (self == NULL)
    {
        return;
    }

    funcs = DTK_ACTOR_CALLBACK(self);
    
    if (funcs->show_all)
    {
        funcs->show_all(self);
    }

    dtk_actor_show(self);
    
    return;
}

void dtk_actor_hide (DTKActor* self)
{
    if (self == NULL)
    {
        return;
    }
    self->visible = DTK_FALSE;
    if (self->funcs.hide)
        self->funcs.hide (self);
    
    dtk_actor_update(self, NULL, DTK_FALSE);
    return;
}

void dtk_actor_hide_all (DTKActor* self)
{
    DTKActorCallback* funcs;
    
    if (self == NULL)
    {
        return;
    }

    funcs = DTK_ACTOR_CALLBACK(self);

    if (funcs->hide_all)
    {
        funcs->hide_all(self);
    }
    self->visible = DTK_FALSE;
    dtk_actor_hide(self);

    return;
}

DTKActor* dtk_actor_get_root(DTKActor* self)
{
    DTKActor* root;

    root = self;
    while(root->parent)
    {
        root = root->parent;
    }

    return root;
}

void dtk_actor_get_region (DTKActor* self, DRegion* region)
{
    if (NULL == self || NULL == region)
    {
        return;
    }

    region->x1 = self->rect.x;
    region->y1 = self->rect.y;
    region->x2 = self->rect.x + self->rect.width;
    region->y2 = self->rect.y + self->rect.height;
    return;
}


void dtk_actor_update (DTKActor* self, DRegion* region, DBoolean flag)
{
    DRegion reg;
    
    if (NULL == self && self->visible != DTK_TRUE)
    {
        return;
    }

    dtk_layout_update (self->layout);

    return;
}

IDirectFBSurface* dtk_actor_get_surface(DTKActor* self)
{
    IDirectFBSurface* surface = NULL;
    DTKActor* actor;
    DFBRectangle rect;
    DFBResult ret;
    
    if (self == NULL)
    {
        return NULL;
    }

    if (self->surface == NULL && self->parent != NULL)
    {
        IDirectFBSurface* parent_surface = NULL;
        parent_surface = dtk_actor_get_surface(self->parent);
        if (parent_surface == NULL)
        {
            return NULL;
        }
        rect.x = self->rect.x;
        rect.y = self->rect.y;
        rect.w = self->rect.width;
        rect.h = self->rect.height;
        ret = parent_surface->GetSubSurface(parent_surface, &rect, &self->surface);
    }

    if (self->funcs.get_surface)
    {
        surface = self->funcs.get_surface (self); 
    }
    else
    {
        surface = self->surface;
    }
   
    return surface;
}

void dtk_actor_release_surface (DTKActor* self)
{
    IDirectFBSurface* surface = NULL;
    DTKActor* actor;
    DFBRectangle rect;
    DFBResult ret;
    
    if (self == NULL || self->surface == NULL)
    {
        return;
    }

    self->surface->Release (self->surface);
    self->surface = NULL;
    return;
}

void dtk_actor_set_bgcolor (DTKActor* self, DColor* color)
{
    if (self == NULL || color == NULL)
    {
        return;
    }
    self->bgcolor = *color;
    return;
}

void dtk_actor_get_bgcolor (DTKActor* self, DColor* color)
{
    if (self == NULL || color == NULL)
    {
        return;
    }
    *color = self->bgcolor;
    return;
}

static void actor_set_bgcolor (DTKActor* self, DValue* value, DValue* old_value)
{
    if (self == NULL || value == NULL || value->type != D_TYPE_COLOR)
    {
        return;
    }

    if (old_value != NULL)
    {
        DColor color;
        dtk_value_set_color(old_value, &self->bgcolor);
    }

    self->bgcolor = dtk_value_get_color(value);
    return;
}

static void actor_get_bgcolor (DTKActor* self, DValue* value)
{
    if (self == NULL || value == NULL)
    {
        return;
    }

    dtk_value_set_color (value, &self->bgcolor);
    return;
}

DTKActor* dtk_actor_init (DInt size, DString type,
    DTKActorCaps caps)
{
    DTKActor* actor;

    if (size < sizeof (DTKActor) || type == NULL)
    {
        return NULL;
    }
    
    actor = (DTKActor*)dtk_object_init(size);

    actor->type = type;
    actor->opacity = 0xff;
    actor->is_initialized = DTK_TRUE;
    actor->layout = dtk_normal_layout_new(actor);
    dtk_link_init(&actor->link, (DPointer*)actor);

    dtk_actor_prop_register(actor, "bgcolor", DPP_RDWR, D_TYPE_COLOR,
        actor_set_bgcolor, actor_get_bgcolor);
   
    return actor;
}

void dtk_actor_finalize (DTKActor* self)
{
    if (self == NULL)
    {
        return;
    }

    if (self->surface)
    {
        self->surface->Release(self->surface);
    }

    if (self->funcs.release)
    {
        self->funcs.release (self);
    }

    if (self->layout)
    {
        dtk_layout_release(self->layout);
    }

    dtk_object_finalize(self);

    return;
}

void dtk_actor_button_down (DTKActor* self, DInt x, DInt y)
{
    DTKMouseButtonEventArgs ev_args;
    DTKLayout* layout;

    layout = dtk_layout_hittest(self->layout, x, y);    

    if (layout)
    {
        DTKActor* actor = NULL;
        actor = dtk_layout_get_actor(layout);

        ev_args.handled = DTK_FALSE;
        ev_args.point.x = x;
        ev_args.point.y = y;
        ev_args.source = actor;

        while (layout)
        {
            //printf ("%s:actor(%s)\n", __func__, actor->type);

            if (actor->funcs.button_down)
            {
                DTK_ACTOR_CALLBACK(actor)->button_down(actor, &ev_args);
            }

            if (ev_args.handled == DTK_FALSE)
            {
                layout = dtk_layout_get_parent(layout);
                actor = dtk_layout_get_actor(layout);
            }
            else
            {
                break;
            }
        }
    }
    return;
}

DTKActor* dtk_actor_button_up (DTKActor* self, DInt x, DInt y)
{
    DTKActor* actor = NULL;

    DTKMouseButtonEventArgs ev_args;
    DTKLayout* layout;

    layout = dtk_layout_hittest(self->layout, x, y);    

    if (layout)
    {
        DTKActor* actor = NULL;
        actor = dtk_layout_get_actor(layout);

        ev_args.handled = DTK_FALSE;
        ev_args.point.x = x;
        ev_args.point.y = y;
        ev_args.source = actor;

        while (layout)
        {
            //printf ("%s:actor(%s)\n", __func__, actor->type);

            if (actor->funcs.button_up)
            {
                DTK_ACTOR_CALLBACK(actor)->button_up(actor, &ev_args);
            }

            if (ev_args.handled == DTK_FALSE)
            {
                layout = dtk_layout_get_parent(layout);
                actor = dtk_layout_get_actor(layout);
            }
            else
            {
                break;
            }
        }
    }
    return;
}

DTKLayout* dtk_actor_get_layout (DTKActor* self)
{
    if (self == NULL)
    {
        return NULL;
    }


    return self->layout;

}

void dtk_actor_set_layout (DTKActor* self, DTKLayout* layout)
{
    DInt count;
    if (self == NULL)
    {
        return;
    }

    if (dtk_layout_get_parent(self->layout))
    {
        return;
    }

    count = dtk_list_get_count(dtk_layout_get_list(self->layout));
    if (count > 0)
    {
        return;
    }    

    dtk_layout_release(self->layout);

    self->layout = layout;

    return;
}

DBoolean dtk_actor_is_visiable (DTKActor* self)
{
    if (self == NULL)
    {
        return DTK_FALSE;
    }

    return self->visible;    
}

void dtk_actor_draw (DTKActor* self, DTKGC* gc)
{
    IDirectFBSurface* surface;
    DRegion reg;
    
    if (self == NULL || gc == NULL)
    {
        return;
    }

    surface = gc->surface;

    dtk_layout_get_bounds (self->layout, &reg);

    if (self->bgcolor.alpha != 0)
    {
        surface->SetColor (surface,
                    self->bgcolor.red,
                    self->bgcolor.green,
                    self->bgcolor.blue,
                    self->bgcolor.alpha);
        surface->FillRectangle (surface,
                    reg.x1, reg.y1, reg.x2 - reg.x1, reg.y2 - reg.y1);
    }

   
    dtk_layout_get_content_region (self->layout, &reg);
    
    if (self->funcs.draw)
    {
        self->funcs.draw (self, gc, &reg);
    }

    return;
}

void dtk_actor_set_margin (DTKActor* self, DInt left, DInt top, DInt right, DInt bottom)
{
    DThickness margin;
    if (self == NULL)
    {
        return;
    }

    margin.left = left;
    margin.top = top;
    margin.right = right;
    margin.bottom = bottom;

    dtk_layout_set_margin (self->layout, margin);
    return;    
}

void dtk_actor_set_zOrder (DTKActor* self, DInt zIndex)
{
    if (self == NULL)
    {
        return;
    }

    dtk_layout_set_zOrder (self->layout, zIndex);

    return;
}

DLink* dtk_actor_get_link(DTKActor* self)
{
    if (self == NULL)
    {
        return NULL;
    }

    return &self->link;
}

void dtk_actor_add (DTKActor* self, DTKActor* first_actor, ...)
{
    va_list args;
    DTKActor* actor;
    if (self == NULL || first_actor == NULL)
    {
        return;
    }

    if (self->funcs.add == NULL)
    {
        return;
    }
   
    va_start (args, first_actor);

    actor = first_actor;

    while (actor)
    {
        if (actor->parent == NULL)
        {
            self->funcs.add(self, actor);
            actor = va_arg(args, DTKActor*);
        }
    }    
    
    va_end(args);
    
    return;
}

void dtk_actor_remove (DTKActor* self, DTKActor* first_actor, ...)
{
    va_list args;
    DTKActor* actor;
    if (self == NULL || first_actor == NULL)
    {
        return;
    }

    if (self->funcs.remove == NULL)
    {
        return;
    }
   
    va_start (args, first_actor);

    actor = first_actor;

    while (actor)
    {
        if (actor->parent != NULL)
        {
            self->funcs.remove(self, actor);
            actor = va_arg(args, DTKActor*);
        }
    }    
    
    va_end(args);
    
    return;
}
